home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 2 / Apprentice-Release2.iso / Source Code / C / Snippets / GIF / GIF.c
Encoding:
Text File  |  1994-04-16  |  18.6 KB  |  403 lines  |  [TEXT/KAHL]

  1. // ----- A GIF reading routine - by François Pottier <pottier@dmi.ens.fr>
  2. // ----- April 16th, 1994
  3. // ----- Best viewed in Geneva 9, with 4 character tabs
  4.  
  5. // ----- Originally based on code by Patrick J. Naughton, greatly modified since then.
  6.  
  7. // ----- Input : a file specification and a flag longword to be passed to NewGWorld
  8. // ----- The file size is also given as input because in my case the caller happens to have it at hand.
  9. // ----- Output : an 8 bit deep GWorld with the picture. Pixels locked.
  10.  
  11. // ----- The routine is rather fast, and uses a minimum amount of memory: only about 15K (plus the GWorld size, of course),
  12. // ----- whatever the GIF file size. It uses a buffer to read chunks of the file - you can change the buffer size by modifying the
  13. // ----- BufferSize #define.
  14.  
  15. // ----- Note on reliability: I have been using this code in a shareware product for several months and it works fine.
  16. // ----- IMHO this is good quality code.
  17.  
  18. // ----- You can use this code freely. I only have two requests: 1. that you give me proper credit in your About box, and
  19. // ----- 2. that you tell me if you improve this code. Have fun!
  20.  
  21. extern    OSErr    gError;                                            /* Imported in order to */
  22. extern    short    gMessage;                                            /* be able to report errors */
  23.  
  24. #define    strCantOpenData        128                                    /* Constants for gMessage */
  25. #define    strGIFNoMem            129                                    /* You can change them, and you ought to move */
  26. #define    strCantRead            130                                    /* them to a global #include file */
  27. #define    strInvalidGIFSig        131                                    /* so that the caller sees them too */
  28. #define    strCantSetFPos            132
  29. #define    strUnknownGIFBlock        133
  30. #define    strNoColorMap            134
  31.  
  32. void DrawGIF (FSSpec *spec, long FileSize, long worldFlags, GWorldPtr *world);    /* Main routine prototype */
  33.  
  34. // ----- A small note about programming style : I would have liked to use
  35. // ----- nested subroutines, but C doesn't have them. I could have allocated all my vars in a struct and passed them along to every
  36. // ----- subroutine, but it seemed painful, so I finally chose to use macros. What's more, this code is optimized for speed (or at
  37. // ----- least it tries to be), so it isn't very readable. My apologies.
  38.  
  39. #define         ColorMapMask        0x80
  40. #define        ImageSeparator        0x2C
  41. #define        ExtensionIntroducer    0x21
  42. #define        NullBlock            0x00
  43. #define        InterlaceMask        0x40
  44. #define        BufferSize        8192                                /* May be freely modified */
  45.  
  46. #define        abort(x)            { gMessage = x; goto end; }
  47. #define        Check(x)            if (e) abort(x)                            /* this means return e */
  48.  
  49. #define        Read(c, buf)        count = c;                                                                    \
  50.                             e = FSRead (refNum, &count, buf);            /* Read the requested number of bytes */    \
  51.                             Check(strCantRead)                                                                    \
  52.                             FileSize -= count                        /* And note that we have read them */
  53.                         
  54. #define        NextByte(b)        Read(1, &b)
  55.  
  56. #define        Skip(n)            e = SetFPos (refNum, fsFromMark, n);        /* Skip n bytes */                        \
  57.                             Check(strCantSetFPos)                                                                    \
  58.                             FileSize -= n                            /* And note that we have read them */
  59.                             
  60. // ----- Here is how things work. BufferBase is the bottom of the buffer, BufferTop is its top. BufferPointer represents the
  61. // ----- byte we're about to read. BufferGetOne is supposed to increase BufferPointer, and if necessary, fill again the buffer.
  62. // ----- If we have to fill the buffer : we read in BufferSize bytes if they are available, otherwise we read till the file's EOF.
  63. // ----- Since a GIF file is made up of stupid blocks, each one with a length byte, we have to ignore the length bytes. We use
  64. // ----- the BlockCount variable : everytime it's zero, we're on a length byte, and we must skip it.
  65.  
  66. // ----- If this seems too complicated, just forget about it. The main goal of all this is to be able to use very little memory
  67. // ----- (the buffer size is adjustable and can be less than 1k) whereas the original algorithm required twice the file size.
  68.  
  69. #define        FillBuffer            {                                                                        \
  70.     BufferPointer = BufferBase;                                                                            \
  71.     Read (BufferSize <= FileSize ? BufferSize : FileSize, BufferBase);                                                \
  72. }
  73.  
  74. #define        BufferGetOne        {                                                                        \
  75.     if (++BufferPointer >= BufferTop)                                    /* If we are at the end of the buffer */        \
  76.         FillBuffer                                                    /* Then fill it again */                    \
  77.     if (BlockCount == 0) {                                            /* If the next byte is a length byte */        \
  78.         BlockCount = *(BufferPointer++);                                /* Then skip it and update BlockCount */        \
  79.         if (BufferPointer == BufferTop)                                    /* If this brings us to the end of the buffer */    \
  80.             FillBuffer                                                /* Then fill it again */                    \
  81.     }                                                                                                \
  82.     BlockCount--;                                                                                        \
  83. }
  84.  
  85. // ----- Since the length bytes are taken out by BufferGetOne, ReadCode can consider the buffer as a continuous flow of raw bytes.
  86. // ----- Here is how it works : BufferPointer points to the byte we're reading, and BitOffset points to the bit inside that byte.
  87. // ----- The convention is that if BitOffset = 0, then a new byte needs to be read.
  88. // ----- We have to read exactly CodeSize bits. So we read as many bytes as are necessary (by comparing BitOffset + CodeSize
  89. // ----- with 8 and 16), then all we have to do is shift right to kill the bits that were part of the last code, and mask the left side
  90. // ----- to mask off the bits that will be part of the next code.
  91.  
  92. #define        ReadCode(code)        {                                                                        \
  93.     if (BitOffset == 0) BufferGetOne                                                                        \
  94.     data = *BufferPointer;                                            /* Read the current byte */                \
  95.     newBitOffset = BitOffset + CodeSize;                                                                    \
  96.     if (newBitOffset > 8) {                                            /* If we need more, read the next byte */    \
  97.         BufferGetOne                                                                                    \
  98.         data += ((long) (*BufferPointer)) << 8;                                                                        \
  99.     }                                                                                                \
  100.     if (newBitOffset > 16) {                                            /* If we still need more, read a third byte */    \
  101.         BufferGetOne                                                                                    \
  102.         data += ((long) (*BufferPointer)) << 16;                                                                    \
  103.     }                                                                                                \
  104.     data >>= BitOffset;                                                /* Skip the bits we already processed */        \
  105.     BitOffset = newBitOffset & 7;                                        /* Compute the new bit offset */            \
  106.     code = data & ReadMask;                                            /* Mask off the bits we don't want yet */    \
  107. }
  108.  
  109. // ----- Since QuickDraw is much too slow for pixel drawing, we blast bits ourselves to the offscreen pixmap. We are able to
  110. // ----- do things very fast by using a pointer, 'curAddr', walking the pixmap. The macro below needs to be in 32 bit addressing
  111. // ----- mode in order to run.
  112.  
  113. #define        DrawPixel(index)        {                                                                    \
  114.     * (Byte*) curAddr = index;                                        /* Write out the pixel */                    \
  115.     curAddr = (long *) ((Byte*) curAddr + 1);                            /* Update the pointer */                    \
  116.     if (++xc == Width) {                                                /* Update the x-coordinate */                \
  117.         xc = 0;                                                    /* If it overflows, update the y-coordinate */    \
  118.         if (!Interlaced)                                                /* In a non-interlaced picture, just */        \
  119.             yc++;                                                /* increment yc to the next scan line */        \
  120.         else {                                                                                        \
  121.             switch (Pass) {                                            /* Otherwise deal with the interlace as */    \
  122.             case 0:                                                /* described in the GIF spec */                \
  123.                 yc += 8;                                                                                \
  124.                 if (yc >= Height) {    Pass++;    yc = 4;    }                                                    \
  125.                 break;                                                                                \
  126.             case 1:                                                                                    \
  127.                 yc += 8;                                                                                \
  128.                 if (yc >= Height) {    Pass++;    yc = 2;    }                                                    \
  129.                 break;                                                                                \
  130.             case 2:                                                                                    \
  131.                 yc += 4;                                                                                \
  132.                 if (yc >= Height) {    Pass++;    yc = 1;    }                                                    \
  133.                 break;                                                                                \
  134.             case 3:                                                                                    \
  135.                 yc += 2;                                                                                \
  136.                 break;                                                                                \
  137.             default:                                                                                    \
  138.                 break;                                                                                \
  139.             }                                                                                        \
  140.         }                                                                                            \
  141.         curAddr = (long*) ((long) srcBaseAddr + (long) yc * (long) rowBytes);    /* update current address in bitmap */        \
  142.     }                                                                                                \
  143. }
  144.  
  145. // ----- ReadColorMap processes the flag byte 'flags' and reads the color map if one is present, and creates a CTabHandle.
  146. // ----- This code is used for the global and local color maps. If both maps are present, the local one overrides the global one.
  147. // ----- The resulting CTabHandle is used when creating the GWorld - this way, color codes in the file and in the pixmap have
  148. // ----- the same meaning.
  149.  
  150. #define        ReadColorMap            {                                                                    \
  151.     Boolean        mapPresent;                                                                            \
  152.     CTabPtr        tablePtr;                                                                                \
  153.     short        i;                                                                                    \
  154.                                                                                                     \
  155.     mapPresent = (flags & ColorMapMask) ? true : false;                    /* Does the file have a global color map ? */    \
  156.     if (mapPresent) {                                                                                    \
  157.         BitsPerPixel = (flags & 7) + 1;                                    /* Image bit depth */                    \
  158.         ColorMapSize = 1 << BitsPerPixel;                                /* Number of entries in color table */        \
  159.         BitMask = ColorMapSize - 1;                                                                        \
  160.                                                                                                     \
  161.         if (ColorTable != NULL)                                        /* The local table overrides the global one */    \
  162.             DisposeHandle ((Handle) ColorTable);                            /* so let's get rid of the latter if it exists */    \
  163.         ColorTable = (CTabHandle) NewHandle (8*ColorMapSize+8);            /* Allocate memory for the table */        \
  164.         if (ColorTable == NULL) { e = memFullErr; abort(strGIFNoMem) }                                                            \
  165.         HLock((Handle) ColorTable);                                                                                \
  166.         tablePtr = *ColorTable;                                                                            \
  167.         tablePtr->ctSeed = 0;                                        /* I don't know what to put in there */        \
  168.         tablePtr->ctFlags = 0;                                        /* so I just zero these fields */            \
  169.         tablePtr->ctSize = ColorMapSize - 1;                                                                \
  170.                                                                                                     \
  171.         for (i = 0; i < ColorMapSize; i++) {                                                                    \
  172.             tablePtr->ctTable[i].value = i;                                                                    \
  173.             NextByte(b);                                                                                \
  174.             tablePtr->ctTable[i].rgb.red = (short) b * 0x100;                /* Determine RGB value */                \
  175.             NextByte(b);                                                                                \
  176.             tablePtr->ctTable[i].rgb.green = (short) b * 0x100;                                                    \
  177.             NextByte(b);                                                                                \
  178.             tablePtr->ctTable[i].rgb.blue = (short) b * 0x100;                                                    \
  179.         }                                                                                            \
  180.         HUnlock((Handle) ColorTable);                                                                                \
  181.     }                                                                                                \
  182.     HasColorMap = HasColorMap || mapPresent;                                                                \
  183. }
  184.  
  185. // ----- Here comes the main routine.
  186.  
  187. void DrawGIF (FSSpec *spec, long FileSize, long worldFlags, GWorldPtr *world)
  188. {
  189.     OSErr        e;                                                /* Error code */
  190.     short        refNum;                                            /* File reference number */
  191.     long            sig;
  192.     long            count;
  193.     Byte            b, flags, c;
  194.     
  195.     short        BitsPerPixel, ColorMapSize, BitMask;
  196.     short        Width, Height;
  197.     short        ClearCode, FreeCode, EOFCode, FirstFree;
  198.     Byte            CodeSize, InitCodeSize;
  199.     short        MaxCode;
  200.     
  201.     Byte            *BufferBase = NULL;                                    /* Read buffer */
  202.     Byte            *BufferPointer, *BufferTop;                            /* Offset in buffer */
  203.     Byte            BlockCount;                                        /* Remaining bytes in current block */
  204.     Byte            BitOffset, newBitOffset;
  205.     long             data;
  206.     short        ReadMask;                                        /* Mask with exactly CodeSize bits set to 1 */
  207.     
  208.     short        CurCode, InCode, OldCode, Code;                        /* Decompressor variables */
  209.     short        FinChar;
  210.     short        *OutCodeBase = NULL;                                /* Output array used by the decompressor */
  211.     short        *OutCode;                                            /* Current pointer into this array */
  212.     short        *Prefix = NULL;                                    /* The hash table used by the decompressor */
  213.     short        *Suffix = NULL;
  214.     
  215.     short        xc, yc;                                            /* Pen position */
  216.     short        Pass;                                            /* Used by the output routines for interlaced pics */
  217.         
  218.     Boolean        HasColorMap, Interlaced;
  219.     
  220.     PixMapHandle    srcPixMap;                                        /* These are associated with 'world' */
  221.     long            *srcBaseAddr, *curAddr;                            /* Pixmap base address and current address */
  222.     short        rowBytes;
  223.     Rect            bounds;
  224.     CTabHandle    ColorTable = NULL;                                    /* Custom color table */
  225.     
  226.     GWorldPtr        oldWorld;                                            /* Some temporary variables */
  227.     GDHandle        oldDevice;
  228.     
  229.     if (e = FSpOpenDF (spec, fsRdPerm, &refNum))                            /* Open the file */
  230.         abort(strCantOpenData)
  231.     
  232. // ----- Allocate temporary buffer and arrays.
  233.  
  234.     if (!(OutCodeBase = (short*) NewPtr(2050)))        { e = MemError(); abort(strGIFNoMem) }
  235.     if (!(Prefix = (short*) NewPtr(8192)))            { e = MemError(); abort(strGIFNoMem) }
  236.     if (!(Suffix = (short*) NewPtr(8192)))            { e = MemError(); abort(strGIFNoMem) }
  237.     if (!(BufferBase = (Byte*) NewPtr(BufferSize)))    { e = MemError(); abort(strGIFNoMem) }
  238.     
  239. // ----- Read variables from the global GIF descriptor
  240.     
  241.     Read (4, &sig);                                                    /* Read the GIF signature */
  242.     if (sig != 'GIF8') { e = paramErr; abort(strInvalidGIFSig) }                    /* and make sure it's correct */
  243.     Skip(6);                                                        /* Skip screen dimensions */
  244.     NextByte(flags);                                                /* This flag byte is processed below */
  245.     Skip(2);                                                        /* Skip background color and aspect ratio */
  246.     
  247.     ReadColorMap                                                    /* Read the global color map */
  248.     
  249. // ----- In an attempt to support GIF89 pictures, we recognize extension blocks and skip them.
  250. // ----- An extension block starts with ExtensionIntroducer. It is followed by any number of data blocks, the last of them being
  251. // ----- a trailer block.
  252.  
  253.     while (true) {
  254.         NextByte(b);                                                /* Read the first byte of the block */
  255.         
  256.         switch (b) {
  257.         case ImageSeparator:                                        /* Image separator, let's go read the image */
  258.             goto ReadImage;
  259.         case ExtensionIntroducer:                                        /* Extension block */
  260.             Skip(1);                                                /* Skip the extension label */
  261.             NextByte(b);                                            /* Get the block size */
  262.             Skip(b);                                                /* Skip the block */
  263.             
  264.             NextByte(b);                                            /* Read the subsequent data blocks */
  265.             while (b != NullBlock) {                                    /* until a trailer block is reached */
  266.                 Skip(b);                                            /* Skip this block */
  267.                 NextByte(b);                                        /* Get length of the next block */
  268.             }
  269.             
  270.             break;
  271.         default:                                                    /* Unknown file format */
  272.             e = paramErr;
  273.             abort(strUnknownGIFBlock);
  274.         }
  275.     }
  276.     
  277. // ----- Read the image descriptor
  278.  
  279. ReadImage:
  280.     Skip(4);                                                        /* Skip left and top offsets */
  281.     NextByte(b); NextByte(c);                                        /* Read width and height */
  282.     Width = (short) c * 0x100 + b;                                        /* Byte per byte because they are written */
  283.     NextByte(b); NextByte(c);                                        /* in stupid PC-like big-endian order */
  284.     Height = (short) c * 0x100 + b;
  285.  
  286.     NextByte(flags);
  287.     Interlaced = (flags & InterlaceMask) ? true : false;                        /* See if the image is interlaced */
  288.  
  289.     ReadColorMap                                                    /* If there is a local color map, read it */
  290.     
  291.     if (!HasColorMap) { e = paramErr; abort(strNoColorMap) }                    /* Make sure we have at least one color map */
  292.     
  293. // ----- Now that we know the picture size, we can create the GWorld. We make it 8 bit deep because most GIF files are 8 bit deep,
  294. // ----- and also because it makes life much easier for DrawPixel : one byte = one pixel. Afterwards, a simple CopyBits call will
  295. // ----- enable the caller to rescale/dither the image.
  296. // ----- The right way would be to switch to 32 bit mode before accessing the pixmap. But calling SwapMMUMode everytime in the
  297. // ----- loop is *very* time consuming. So I check to see if we are in 24 bit mode, and if that is the case, then I force the pixmap
  298. // ----- to be stored in main memory by using the keepLocal flag. This way I make certain that the pixmap can be accessed
  299. // ----- in 24 bit mode.
  300.  
  301.     GetGWorld (&oldWorld, &oldDevice);                                    /* save the current world */
  302.     SetRect (&bounds, 0, 0, Width, Height);                                /* and create our own */
  303.     if (e = NewGWorld(world, 8, &bounds, ColorTable, NULL, worldFlags | (GetMMUMode() == false32b ? keepLocal : 0)))
  304.         abort(strGIFNoMem)
  305.     LockPixels(GetGWorldPixMap(*world));                                /* Lock the pixel map */
  306.     SetGWorld (*world, NULL);                                        /* activate it */
  307.         
  308. // ----- Start reading the raster data. First initialize the decoder.
  309.  
  310.     NextByte(CodeSize);
  311.     ClearCode = (1 << CodeSize);
  312.     EOFCode = ClearCode + 1;
  313.     FreeCode = FirstFree = ClearCode + 2;
  314.     
  315. // ----- The GIF spec has it that the code size used to compute the above values is the code size given in the file, but the
  316. // ----- code size used in compression/decompression is the code size given in the file plus one. (thus the ++).
  317.  
  318.     CodeSize++;
  319.     InitCodeSize = CodeSize;
  320.     MaxCode = (1 << CodeSize);
  321.     ReadMask = MaxCode - 1;
  322.  
  323.     BlockCount = 0;                                                    /* We start at the beginning of a block */
  324.     BitOffset = 0;                                                    /* and at the beginning of a byte */
  325.     BufferPointer  = BufferTop = BufferBase + BufferSize;                    /* Force the buffer to be filled immediately */
  326.     
  327.     xc = yc = 0;
  328.     Pass = 0;
  329.             
  330. // ----- Decompress the file, continuing until a GIF EOF code is reached.
  331.  
  332.     srcPixMap = GetGWorldPixMap(*world);                                /* Get the pixmap */
  333.     rowBytes = (*srcPixMap)->rowBytes & 0x7FFF;                        /* get rowBytes */
  334.     curAddr = srcBaseAddr = (long*) GetPixBaseAddr (srcPixMap);            /* and the base address of the pixmap */
  335.     
  336.     ReadCode(Code);
  337.     while (Code != EOFCode) {                            
  338.         if (Code == ClearCode) {                                        /* Clear code sets everything back to its */
  339.             CodeSize = InitCodeSize;                                    /* initial value, then reads the subsequent code */
  340.             MaxCode = (1 << CodeSize);                                /* as uncompressed data. */
  341.             ReadMask = MaxCode - 1;
  342.             FreeCode = FirstFree;
  343.             ReadCode(Code);
  344.             CurCode = OldCode = Code;
  345.             FinChar = CurCode & BitMask;
  346.             DrawPixel(FinChar);
  347.         }
  348.         else {                                                    /* If not Clear code, then must be data. */
  349.             CurCode = InCode = Code;                                    /* Save same as CurCode and InCode */
  350.  
  351.             OutCode = OutCodeBase;
  352.             if (CurCode >= FreeCode) {                                /* If >= FreeCode, not in the hash table yet */
  353.                 CurCode = OldCode;                                    /* repeat the last character decoded */
  354.                 *(OutCode++) = FinChar;
  355.             }
  356.             
  357.             while (CurCode > BitMask) {                                /* Pursue the chain pointed to by CurCode */
  358.                 *(OutCode++) = Suffix[CurCode];                        /* through the hash table to its end */
  359.                 CurCode = Prefix[CurCode];                            /* the output queue */
  360.             }
  361.  
  362.             FinChar = CurCode & BitMask;                                /* The last code in the chain is treated as raw data. */
  363.             *OutCode = FinChar;
  364.                         
  365.             while (OutCode >= OutCodeBase)                            /* Now we put the data out */
  366.                 DrawPixel(*(OutCode--));                            /* It's been stacked LIFO, so deal with it that way */
  367.  
  368.             Prefix[FreeCode] = OldCode;                                /* Build the hash table on-the-fly */
  369.             Suffix[FreeCode] = FinChar;                                /* No table is stored in the file */
  370.             OldCode = InCode;
  371.             
  372.             if (++FreeCode >= MaxCode)                                /* Point to the next slot in the table */
  373.                 if (CodeSize < 12) {                                    /* If we exceed the current MaxCode value */
  374.                     CodeSize++;                                    /* increment the code size unless it's already 12 */
  375.                     MaxCode *= 2;                                    /* If it is, do nothing; the next code better be */
  376.                     ReadMask = MaxCode - 1;                            /* Clear */
  377.                 }                                                
  378.         }
  379.  
  380.         ReadCode(Code);                                            /* Read the next code */
  381.     }
  382.     
  383.     SetGWorld (oldWorld, oldDevice);                                    /* restore the previous graphics world */
  384.     
  385. // ----- Release temporary buffer and arrays, close file.
  386.  
  387.     e = noErr;
  388.  
  389. end:
  390.     if (ColorTable)                                                    /* We can destroy the CTable, NewGWorld */
  391.         DisposeHandle((Handle) ColorTable);                                /* copied it */
  392.     if (BufferBase)
  393.         DisposePtr((Ptr) BufferBase);
  394.     if (Suffix)
  395.         DisposePtr((Ptr) Suffix);
  396.     if (Prefix)
  397.         DisposePtr((Ptr) Prefix);
  398.     if (OutCodeBase)
  399.         DisposePtr((Ptr) OutCodeBase);
  400.  
  401.     gError = e;
  402.     return;
  403. }